#include "stdafx.h"
#include "Enemy.h"
#include "Collider.h"
#include "TargetMediator.h"
#include "AttackWallMediator.h"
#include "EntityFactory.h"
#include "EntityManager.h"
#include "EnemyProjectile.h"

Enemy::Enemy(const EEntityType& entity_type, TargetMediator* target_mediator, AttackWallMediator* attack_wall_mediator, EntityFactory* entity_factory, EntityManager* entity_manager) 
	: Entity(entity_type), m_target_mediator(target_mediator), m_attack_wall_mediator(attack_wall_mediator), m_entity_factory(entity_factory), m_entity_manager(entity_manager){}
Enemy::~Enemy(void){ m_target_mediator->RemoveTarget(this); }

using namespace DirectX;

void Enemy::Init(SceneObject* scene_object, Grid* grid)
{
	m_scene_object = scene_object;
	m_scene_object->m_transform = XMMatrixRotationRollPitchYaw(m_rotation.x, m_rotation.y, m_rotation.z);

	m_grid = grid;

	m_speed = 10.0f;
	m_slow_speed = m_speed * 0.5f;
	m_direction = Vector3(0.0f, 0.0f, 0.0f);
	m_rot_speed = 10.0f;
	m_health = 2;
	SetShootTime(2.0f);
	m_curr_shoot_time = 0.0f;
	SetSlowTime(2.0f);
	ResetSlowTime();
	
	m_status = EStatus::Normal;
}

void Enemy::Update(float deltatime)
{
	if (m_status == EStatus::Slow)
	{
		if (m_curr_slow_time > 0.0f)
			m_curr_slow_time -= deltatime;
		else
		{
			SetStatus(EStatus::Normal);
			ResetSlowTime();
		}
	}

	if (m_direction.x == 0.0f && m_direction.y == 0.0f && m_direction.z == 0.0f)
	{
		if (m_waypoint.WaypointsLeft())
		{
			//Find the next waypoint
			m_next_location = m_waypoint.GetNext();
			m_waypoint.PopFront();

			//Get current direction
			XMFLOAT4X4 mat;
			XMStoreFloat4x4(&mat, m_scene_object->m_transform);
			m_direction.x = mat._13;
			m_direction.y = mat._23;
			m_direction.z = mat._33;
			m_direction.Normalize();

			//Which direction we want to look
			m_goal_direction = m_next_location - m_position;
			m_goal_direction.Normalize();

			// If dot product is not negative, then the vectors are paralell.
			// Adjust m_direction a little for 180 degree rotation.
			// Alternative solution: Use cross product and see if it is zero.
			if (cosf( (m_goal_direction.x * m_direction.x) + (m_goal_direction.y * m_direction.y) + (m_goal_direction.z * m_direction.z) ) >= 0.0f )
			{
				m_direction.x += 0.1f;
				m_direction.z += 0.1f;
				m_direction.Normalize();
			}
		}
		else
		{
			// Out of waypoints
			Entity* target = m_attack_wall_mediator->GetTarget();
			Shoot(target, deltatime);
			if (m_attack_wall_mediator->GetTarget() == nullptr)
				RemoveMe();
		}
	}
	else
	{
		//Distance to the goal
		float distanceX = m_next_location.x - m_position.x;
		float distanceZ = m_next_location.z - m_position.z;
		m_distance = sqrtf( (distanceX * distanceX) + (distanceZ * distanceZ) );

		//Movement
		float movement;
		if (m_status == EStatus::Slow)
			movement = m_slow_speed * deltatime;
		else
			movement = m_speed * deltatime;
		m_position += (m_direction * movement);

		//Transform
		SetMatrixValues(deltatime);
		m_scene_object->m_transform = XMLoadFloat4x4(&m_transform_mat);

		//Reached the goal
		if (m_distance < 1.0f)
			m_direction = Vector3(0.0f, 0.0f, 0.0f);
	}

	// Move bounding sphere with entity
	m_scene_object->m_bounding_sphere.m_position.x = m_position.x;
	m_scene_object->m_bounding_sphere.m_position.y = m_position.y;
	m_scene_object->m_bounding_sphere.m_position.z = m_position.z;
	m_collider->Update(m_position);

	if (m_health <= 0)
		RemoveMe();
}

void Enemy::AddWaypoint(Waypoint waypoint)
{
	m_waypoint = waypoint;
}

void Enemy::SetMatrixValues(float deltatime)
{
	XMStoreFloat4x4(&m_transform_mat, m_scene_object->m_transform);

	//Which direction we want to look
	m_goal_direction = m_next_location - m_position;
	m_goal_direction.Normalize();

	XMVECTOR d = { m_direction.x, m_direction.y, m_direction.z },
		gd = { m_goal_direction.x, m_goal_direction.y, m_goal_direction.z };

	//Set the new direction
	d = XMVectorLerp(d, gd, m_rot_speed * deltatime);
	Vec3 lerp_direction;
	XMStoreFloat3(&lerp_direction, d);
	m_direction.x = lerp_direction.x;
	m_direction.y = lerp_direction.y;
	m_direction.z = lerp_direction.z;

	//Set the new up and right vector
	Vector3 up= Vector3(0, 1, 0);
	Vector3 right = m_direction.Cross(up);

	m_direction.Normalize();
	right.Normalize();

	m_transform_mat._11 = right.x;
	m_transform_mat._21 = right.y;
	m_transform_mat._31 = right.z;

	m_transform_mat._12 = up.x;
	m_transform_mat._22 = up.y;
	m_transform_mat._32 = up.z;

	m_transform_mat._13 = m_direction.x;
	m_transform_mat._23 = m_direction.y;
	m_transform_mat._33 = m_direction.z;

	m_transform_mat._41 = m_position.x;
	m_transform_mat._42 = m_position.y;
	m_transform_mat._43 = m_position.z;
}

void Enemy::Shoot(Entity* target, float deltatime)
{
	if (m_curr_shoot_time <= 0.0f && target != nullptr)
	{
		Vector3 target_position = target->GetPosition();
		Vector3 direction = target_position - m_position;
		direction.Normalize();

		Entity* projectile = m_entity_factory->Create(Enemy_Projectile, m_grid);
		m_entity_manager->AddEntity(projectile);
		projectile->SetPosition(m_position);

		if (projectile->GetEntityType() == Enemy_Projectile){
			EnemyProjectile* p = static_cast<EnemyProjectile*>(projectile);
			p->SetPosition(m_position);
			p->SetDirection(direction);
		}
		m_curr_shoot_time = m_shoot_time;
		projectile->Update(0.0f);
	}
	else
		m_curr_shoot_time -= deltatime;
}
